package xj.graph2d.app;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import xj.app.Application;
import xj.app.DrawTool;
import xj.app.LookAndFeelManager;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.Group;
import xj.graph2d.OpenShape;
import xj.graph2d.TextShape;
import xj.graph2d.TwoEndsShape;
import xj.graph2d.shapes.CurveShape;
import xj.graph2d.shapes.GraphEdgeShape;
import xj.graph2d.shapes.PolyLineShape;
import xj.graph2d.shapes.PolyShape;
import xj.graph2d.shapes.ShapeIcon;
import xj.graph2d.template.Template;
import xj.graph2d.template.TemplateToolButton;
import xj.graph2d.tools.CurveTool;
import xj.graph2d.tools.OnePointShapeTool;
import xj.graph2d.tools.SimpleDrawTool;
import xj.graph2d.tools.TwoEndsDrawTool;
import xj.graph2d.tools.TwoEndsPolyLineDrawTool;
import xj.util.gui.ResourceManager;
import xj.util.xml.ErrorMessageHandler;
import xj.util.xml.XMLUtil;

public class ToolPanel extends JPanel {

  public ToolPanel(String templateName, String displayName) {
    this(templateName, displayName, 150);
  }

  public ToolPanel(String templateName, String displayName, int width) {
    this.templateName = templateName;
    this.displayName = displayName;

    // setLayout(new FlowLayout());
    preferredWidth = width;
    setMinimumSize(new Dimension(50, 0));
    setPreferredSize(new Dimension(width, 0));
    // setMaximumSize(new Dimension(200, Short.MAX_VALUE));

    // setBackground(UIManager.getColor("window"));
  }

  public String getDisplayName() {
    return displayName;
  }

  public void setDisplayName(String displayName) {
    this.displayName = displayName;
  }

  public String getTemplateName() {
    return templateName;
  }

  public void setTemplateName(String templateName) {
    this.templateName = templateName;
  }

  protected String templateName;

  protected String displayName;

  public static ToolPanel loadTemplate(String templateName, 
				       String displayName,
				       Template template, 
				       ActionListener action, 
				       ButtonGroup group,
				       Application app, 
				       ResourceManager resourceManager, 
				       Map iconmap) {
    ToolPanel toolPanel = null;
    if (template != null) {
      toolPanel = new ToolPanel(templateName, displayName);
      if (group == null) {
	group = new ButtonGroup();
      }
      List<TemplateToolButton> blist = template.getButtonList(TemplateToolButton.TOOL_PANEL_BUTTON);
      int n = blist.size();
      for (int i = 0; i < n; i++) {
	TemplateToolButton b = blist.get(i);
	JToggleButton button = makeShapeButton(templateName, b, action, group,
					       template.isGraphComponent(), 
					       app, resourceManager, iconmap);
	toolPanel.add(button);
      }
    }
    return toolPanel;
  }

  public static ToolPanel loadTemplate(String templateName, 
				       String displayName,
				       String fileName, 
				       ActionListener action, 
				       ButtonGroup group,
				       boolean isgraph, 
				       Application app, 
				       ResourceManager resourceManager,
				       Map iconmap) {
    ToolPanel toolPanel = null;
    if (fileName != null) {
      InputStream templateIn = ToolPanel.class.getResourceAsStream(fileName);
      org.w3c.dom.Document templateDoc = null;
      if (templateIn != null) {
	try {
	  ErrorMessageHandler errorHandler = new ErrorMessageHandler(fileName);
	  templateDoc = XMLUtil.parseXMLDocument(templateIn, errorHandler, false);
	  if (errorHandler.hasErrorOrWarning()) {

	  }
	} catch (IOException e) {
	  System.err.println("Error reading file " + fileName + ": " + e.getMessage());
	} catch (SAXParseException e) {
	  // error in input
	  System.err.println("Error " + fileName + ":" + e.getMessage());
	} catch (SAXException e) {
	  // error in input
	  System.err.println("Error " + fileName + ":" + e.getMessage());
	}
      }

      if (templateDoc != null) {
	Element docElement = templateDoc.getDocumentElement();
	XMLUtil.stripWhiteSpace(docElement);

	toolPanel = new ToolPanel(templateName, displayName);
	if (group == null) {
	  group = new ButtonGroup();
	}
	if ("Template".equals(docElement.getLocalName())) {
	  if (docElement.hasChildNodes()) {
	    NodeList nodes = docElement.getChildNodes();
	    if (nodes != null) {
	      int n = nodes.getLength();
	      for (int i = 0; i < n; i++) {
		Node node = nodes.item(i);
		if (node.getNodeType() == Node.ELEMENT_NODE) {
		  Element e = (Element) node;
		  String ename = e.getLocalName();
		  if ("ShapeButton".equals(ename)) {
		    JToggleButton button = makeShapeButton(templateName, e,
							   action, group, isgraph, 
							   app, resourceManager, iconmap);
		    toolPanel.add(button);
		  }
		}
	      }
	    }
	  }
	}
      }
    }
    return toolPanel;
  }

  protected static JToggleButton makeShapeButton(String templateName,
						 TemplateToolButton b,
						 ActionListener action, 
						 ButtonGroup group, 
						 boolean isgraph,
						 Application app, 
						 ResourceManager resourceManager, 
						 Map iconmap) {
    if (b != null) {
      return makeShapeButton(templateName, b.shape, b.iconShape, b.name,
			     b.width, b.height, b.selected, b.enabled, 
			     b.toolType, b.reverseEnds,
			     action, group, isgraph, app, resourceManager, iconmap);
    }
    return null;
  }

  protected static JToggleButton makeShapeButton(String templateName,
						 DrawShape shape, 
						 DrawShape iconShape, 
						 String name, 
						 int width, 
						 int height,
						 boolean selected, 
						 boolean enabled, 
						 int toolType, 
						 boolean reverseEnds,
						 ActionListener action,
						 ButtonGroup group, 
						 boolean isgraph, 
						 Application app,
						 ResourceManager resourceManager, 
						 Map iconmap) {
    JToggleButton button = null;
    String command;
    if (templateName != null) {
      command = templateName + "." + name;
    } else {
      command = name;
    }
    shape.setTemplateName(command);

    if (templateName != null && 
	shape instanceof Group) { 
      Group g = (Group) shape;
      List<DrawShape> gs = g.getShapes();
      for (int i = 0; i < gs.size(); i++) { 
	DrawShape s1 = gs.get(i);
	if (s1 != null) { 
	  String sn = s1.getTemplateName();
	  if (sn != null && sn.length() > 0) { 
	    s1.setTemplateName(templateName + "." + sn);
	  }
	}
      }
    }

    DrawShapeFactory factory = DrawShapeFactory.getInstance();
    factory.addPrototype(command, shape);

    if (iconShape == null) {
      iconShape = shape;
    }
    if (iconmap != null) {
      iconmap.put(command, new ShapeIcon(iconShape, 16, 16));

      if (debug) {
	System.out.println("ToolPanel.makeShapeButton(): add icon " + command);
      }
    }

    Icon icon = new ShapeIcon(iconShape, width, height);
    button = new JToggleButton(icon);
    button.putClientProperty(RenderingHints.KEY_TEXT_ANTIALIASING, 
			     RenderingHints.VALUE_ANTIALIAS_ON);

    LookAndFeelManager lnfManager = LookAndFeelManager.getInstance();
    if (lnfManager.isSubstanceLnF()) { 
      button.putClientProperty(org.jvnet.substance.SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY,
			       "org.jvnet.substance.button.ClassicButtonShaper");
    }

    if (showButtonName && name != null) {
      Font f = button.getFont();
      //System.out.println("ToolPanel.makeShapeButton: font=" + f);
      button.setFont(f.deriveFont(Font.PLAIN, 10));

      button.setVerticalTextPosition(SwingConstants.CENTER);
      button.setHorizontalTextPosition(SwingConstants.RIGHT);

      button.setVerticalAlignment(SwingConstants.CENTER);
      button.setHorizontalAlignment(SwingConstants.LEFT);

      if (resourceManager != null) {
	String text = resourceManager.getResourceString("template." + command + ".text");
	String tip = resourceManager.getResourceString("template." + command + ".tip");
	if (tip != null) { 
	  tip = "<html><center>" + tip.replaceAll("\n", "<br/>") + "</center></html>";
	} 
	String dname = resourceManager.getResourceString("template." + command + ".display");
	
	if (debug) {
	  System.out.println("ToolPanel getResourceString: " + command);
	  System.out.println("  text=" + text);
	  System.out.println("  tip=" + tip);
	}

	if (text != null && text.length() > 0) {
	  button.setText(text);
	} else {
	  button.setText(name);
	}
	if (tip != null && tip.length() > 0) {
	  button.setToolTipText(tip);
	}
	if (dname != null && dname.length() > 0) {
	  shape.setDisplayName(dname);
	}
      } else {
	button.setText(name);
      }

    }
    button.setActionCommand(command);
    if (group != null) {
      group.add(button);
    }

    button.setSelected(selected);
    button.setEnabled(enabled);
    SimpleDrawTool tool = makeShapeTool(shape, isgraph, toolType, reverseEnds, command, app);
    tool.setToolButton(button);
    app.addTool(command, tool);
    if (action != null) {
      button.addActionListener(action);
    }

    return button;
  }

  public static SimpleDrawTool makeShapeTool(DrawShape shape, 
					     String command,
					     Application app) { 
    return makeShapeTool(shape, true, TemplateToolButton.DEFAULT_TOOL, false, command, app);
  }

  public static SimpleDrawTool makeShapeTool(DrawShape shape, 
					     boolean reverseEnds, 
					     String command,
					     Application app) { 
    return makeShapeTool(shape, true, TemplateToolButton.DEFAULT_TOOL, reverseEnds, command, app);
  }

  public static SimpleDrawTool makeShapeTool(DrawShape shape, 
					     boolean isgraph,
					     int toolType, 
					     String command,
					     Application app) { 
    return makeShapeTool(shape, isgraph, toolType, false, command, app);
  }

  public static SimpleDrawTool makeShapeTool(DrawShape shape, 
					     boolean isgraph,
					     int toolType, 
					     boolean reverseEnds, 
					     String command,
					     Application app) { 
    //System.out.println("ToolPanel.makeShapeTool() reverseEnds=" + reverseEnds);
    if (app != null) { 
      DrawTool switchTool = app.getTool("Point");
      SimpleDrawTool tool = null;
      if (shape instanceof OpenShape) {
	if (shape instanceof GraphEdgeBase && isgraph) {
	  GraphEdgeBase edge = (GraphEdgeBase) shape;
	  edge.initComponent();
	}
	
	int edgeType;
	if (shape instanceof GraphEdgeShape) { 
	  GraphEdgeShape es = (GraphEdgeShape) shape;
	  GraphEdgeBase eshape = es.getEdgeShape();
	  edgeType = getGraphEdgeType(eshape); 
	} else {
	  edgeType = getGraphEdgeType(shape); 
	}
	tool = getGraphEdgeTool(shape, edgeType, toolType, reverseEnds, command);
	
      } else if (shape instanceof TextShape) {
	// tool = new TextTool((TextShape) shape,
	// DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
	tool = new OnePointShapeTool(shape,
				     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
      } else if (shape instanceof GraphNodeBase) {
	if (isgraph) {
	  GraphNodeBase node = (GraphNodeBase) shape;
	  node.initComponent();
	}
	tool = new OnePointShapeTool(shape,
				     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
	tool.setGraphMode(isgraph);
      } else {
	tool = new OnePointShapeTool(shape,
				     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
      }
      if (switchTool instanceof SimpleDrawTool) {
	tool.setSwitchTool((SimpleDrawTool) switchTool);
      }
      return tool;
    }
    return null;
  }

  protected static final int TWO_ENDS_EDGE_TYPE = 1;
  protected static final int POLYLINE_EDGE_TYPE = 2;
  protected static final int CURVE_EDGE_TYPE    = 3;
  protected static final int OTHER_TYPE         = 0;

  protected static int getGraphEdgeType(DrawShape edge) { 
    int etype = OTHER_TYPE;
    if (edge != null) { 
      if (edge instanceof GraphEdgeShape) { 
	GraphEdgeShape eshape = (GraphEdgeShape) edge;
	edge = eshape.getEdgeShape();
      }
      if (edge instanceof PolyLineShape) {
	etype = POLYLINE_EDGE_TYPE;
      } else if (edge instanceof CurveShape) {
	etype = CURVE_EDGE_TYPE;
      } else if (edge instanceof TwoEndsShape) {
	etype = TWO_ENDS_EDGE_TYPE;
      }
    }
    return etype; 
  }

  protected static SimpleDrawTool getGraphEdgeTool(DrawShape edge, 
						   int edgeType,
						   int toolType, 
						   boolean reverseEnds, 
						   String command) { 
    SimpleDrawTool tool = null;
    if (edgeType == TWO_ENDS_EDGE_TYPE) { 
      tool = new TwoEndsDrawTool(edge,
				 DrawShapeConstants.USE_PROTOTYPE_DRAW_ATTR, 
				 reverseEnds, command);
      tool.setGraphMode(true);
    } else if (edgeType == POLYLINE_EDGE_TYPE) { 
      tool = new TwoEndsPolyLineDrawTool((PolyShape) edge,
					 DrawShapeConstants.USE_PROTOTYPE_DRAW_ATTR, command);
      tool.setGraphMode(true);
    } else if (edgeType == CURVE_EDGE_TYPE) { 
      if (toolType == TemplateToolButton.DEFAULT_TOOL) {
	tool = new CurveTool((CurveShape) edge,
			     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR, 
			     reverseEnds, command, false);
      } else if (toolType == TemplateToolButton.SCALE_TOOL) {
	tool = new CurveTool((CurveShape) edge,
			     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR, 
			     reverseEnds, command, true);
      } else {
	tool = new OnePointShapeTool(edge,
				     DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
      }
      tool.setGraphMode(true);
    } else {
      tool = new OnePointShapeTool(edge,
				   DrawShapeConstants.CLONE_PROTOTYPE_DRAW_ATTR);
    }
    return tool; 
  }

  protected static JToggleButton makeShapeButton(String templateName,
						 Element sb, ActionListener action, 
						 ButtonGroup group, boolean isgraph,
						 Application app, 
						 ResourceManager resourceManager, 
						 Map iconmap) {
    if (sb != null && sb.hasChildNodes()) {
      Node child = sb.getFirstChild();
      if (child.getNodeType() == Node.ELEMENT_NODE) {
	DrawShape shape = DrawShapeBase.makeShapeFromXML((Element) child);
	if (shape != null) {
	  String name = sb.getAttribute("name");
	  String width = sb.getAttribute("width");
	  String height = sb.getAttribute("height");
	  String selected = sb.getAttribute("selected");
	  String enabled = sb.getAttribute("enabled");
	  int iconWidth = 16;
	  int iconHeight = 16;
	  try {
	    iconWidth = Integer.parseInt(width);
	  } catch (NumberFormatException ex) {
	  }
	  try {
	    iconHeight = Integer.parseInt(height);
	  } catch (NumberFormatException ex) {
	  }
	  boolean toolSelected = ("true".equals(selected));
	  boolean toolEnabled = true;
	  if ("false".equals(enabled)) {
	    toolEnabled = false;
	  }
	  return makeShapeButton(templateName, shape, shape, name, iconWidth,
				 iconHeight, toolSelected, toolEnabled, 
				 TemplateToolButton.DEFAULT_TOOL, false,
				 action, group, isgraph, app, 
				 resourceManager, iconmap);
	}
      }
    }
    return null;
  }

  protected int preferredWidth = 150;

  @Override
  public void doLayout() {
    //doLayout1();
    doLayout2();
  }

  int hgap = 0;

  int vgap = 0;

  private void doLayout1() {
    Insets insets = getInsets();
    //int mwidth = 72;
    //int mheight = 64;
    int mwidth = 100;
    int mheight = 36;
    // int maxwidth = Math.max(preferredWidth, getWidth());
    int maxwidth = mwidth * 2 + hgap * 3;
    int nmembers = getComponentCount();
    int i;
    
    /* 
    for (i = 0 ; i < nmembers ; i++) { 
      Component m = getComponent(i); 
      if (m.isVisible()) { 
	Dimension d = m.getPreferredSize(); 
	mwidth = Math.max(mwidth, d.width); 
	mheight = Math.max(mheight, d.height); 
      }
    }
    */
    // System.out.println("ToolPanel.dolayout1() maxwidth=" + mwidth + " maxheight=" + mheight);
    int x = insets.left, y = insets.top + vgap;
    int rowh = mheight;
    boolean first = true;
    for (i = 0; i < nmembers; i++) {
      Component m = getComponent(i);
      m.setSize(mwidth, mheight);
      if (m.isVisible()) {
	if (first || ((x + hgap + mwidth) <= maxwidth)) {
	  x += hgap;
	  first = false;
	} else {
	  x = insets.left + hgap;
	  y += vgap + rowh;
	}
	m.setLocation(x, y);
	x += mwidth;
      }
    }
    setPreferredSize(new Dimension(maxwidth, y + rowh + vgap + insets.bottom));
  }

  private void doLayout2() {
    Dimension dim = getSize(); 
    Insets insets = getInsets();
    //int mwidth = 72;
    //int mheight = 64;
    int mwidth = dim.width - insets.left - insets.right - hgap * 2;
    int mheight = 36;
    // int maxwidth = Math.max(preferredWidth, getWidth());
    int maxwidth = mwidth + hgap * 2;
    int nmembers = getComponentCount();
    int i;
    
    /* 
    for (i = 0 ; i < nmembers ; i++) { 
      Component m = getComponent(i); 
      if (m.isVisible()) { 
	Dimension d = m.getPreferredSize(); 
	mwidth = Math.max(mwidth, d.width); 
	mheight = Math.max(mheight, d.height); 
      }
    }
    */
    // System.out.println("ToolPanel.dolayout1() maxwidth=" + mwidth + " maxheight=" + mheight);
    int x = insets.left, y = insets.top + vgap;
    int rowh = mheight;
    boolean first = true;
    for (i = 0; i < nmembers; i++) {
      Component m = getComponent(i);
      m.setSize(mwidth, mheight);
      if (m.isVisible()) {
	x = hgap;
	m.setLocation(x, y);
	y += vgap + rowh;
      }
    }
    setPreferredSize(new Dimension(maxwidth, y + insets.bottom));
  }


  protected static boolean showButtonName = true;

  protected static final boolean debug = false;

}
